package com.peng.herostory.async;

import com.peng.herostory.MainThreadProcessor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 游戏开发中，为了避免复杂问题出现，将业务放在一个主线程进行， 但其中的IO操作，我们其实依旧可以分离出到别的线程，
 * 但相对的，如果不作改动，在IO操作完毕回归线程业务的时候依旧会出现多线程安全的复杂问题，
 * 因此我们需要在IO操作完毕后，依旧回到主线程去完毕我们的业务逻辑，
 * 这样依旧保证了系统的安全
 */
public final class AsyncThreadProcessor {

    private static AsyncThreadProcessor instance = new AsyncThreadProcessor();

    //IO线程自然是多线程快一些，但如何是正确的使用方式？
    //举个例子，如果一个用户快速点击了多次注册按钮，因为是多线程，因此可能会造成多次注册的问题
    //解决方案？ 很简单， 虽然是多线程，但我们只要根据用户的特征值，让它的全部行为都只会进入一个固定线程
    // 这样全局来看是多线程，但对于一个用户来说，其实所有操作变成了一种串行化操作，避免了多线程的安全问题！
    private ExecutorService[] executors = new ExecutorService[8];


    private AsyncThreadProcessor() {
        for (int i = 0; i < executors.length; i++) {
            int finalI = i;
            executors[i] = Executors.newSingleThreadExecutor((r) -> {
                Thread thread = new Thread(r);
                thread.setName("AsyncThreadProcessor" + finalI);
                return thread;
            });
        }

    }

    public static AsyncThreadProcessor getInstance() {
        return instance;
    }

    public void process(IAsyncOperation op) {
        int bindId = Math.abs(op.getBindId());
        int esIndex = bindId % executors.length;
        executors[esIndex].submit(() -> {
            try {
                op.doAsync();
            } catch (Exception e) {
                e.printStackTrace();
            }
            MainThreadProcessor.getInstance().process(op::doFinish);
        });

    }
}
